﻿using Moq;
using NUnit.Framework;
using Payroll.Biz.Legacy.DAL;
using Payroll.Biz.Legacy.Models;
using Payroll.Biz.Legacy.ServiceParams;
using Payroll.Biz.Legacy.Services;
using System;

namespace Payroll.Biz.Legacy.Test.GPT
{
    /// <summary>
    /// Prompt:
    /// Here is a service class that is under test, could you write unit tests for me?
    /// <code>
    /// public class AddHourlyEmployeeService : AbstractService, IService<AddHourlyEmployeeServiceParams>
    /// {
    ///     public AddHourlyEmployeeService(IPayrollDatabase database) : base(database)
    ///     {
    ///     }
    /// 
    ///     public ServiceResult Execute(AddHourlyEmployeeServiceParams serviceParams)
    ///     {
    ///         IPaymentClassification paymentClassification = new HourlyClassification(serviceParams.HourlyRate);
    /// 
    ///         Guid id = Guid.NewGuid();
    /// 
    ///         Employee employee = EmployeeFactory.Create(
    ///             id,
    ///             serviceParams.Name,
    ///             serviceParams.Address,
    ///             paymentClassification);
    ///         employee.Schedule = new WeeklySchedule();
    ///         employee.Method = new HoldMethod();
    /// 
    ///         Database.AddEmployee(employee);
    /// 
    ///         return ServiceResult.CreateSuccessResult(employee.Id);
    ///     }
    /// }
    /// </code>
    /// </summary>
    [TestFixture]
    public class GenerateUTForLegacyCode
    {
        private Mock<IPayrollDatabase> _mockDatabase;
        private AddHourlyEmployeeService _service;

        [SetUp]
        public void Setup()
        {
            _mockDatabase = new Mock<IPayrollDatabase>();
            _service = new AddHourlyEmployeeService(_mockDatabase.Object);
        }

        [Test]
        public void Execute_ShouldAddEmployeeToDatabase()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = "123 Main St",
                HourlyRate = 25.0m
            };

            // Act  
            ServiceResult result = _service.Execute(serviceParams);

            // Assert  
            Assert.IsTrue(result.IsSuccess);
            _mockDatabase.Verify(
                db => db.AddEmployee(
                    It.Is<Employee>(
                        e => (e.Name == serviceParams.Name) &&
                            (e.Address == serviceParams.Address) &&
                            (e.Classification is HourlyClassification) &&
                            (((HourlyClassification)e.Classification).HourlyRate == serviceParams.HourlyRate) &&
                            (e.Schedule is WeeklySchedule) &&
                            (e.Method is HoldMethod))),
                Times.Once);
        }

        [Test]
        public void Execute_ShouldReturnSuccessResultWithEmployeeId()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "Jane Doe",
                Address = "456 Elm St",
                HourlyRate = 30.0m
            };

            // Act  
            ServiceResult result = _service.Execute(serviceParams);

            // Assert  
            Assert.IsTrue(result.IsSuccess);
            Assert.AreNotEqual(Guid.Empty, result.EmployeeId);
        }

        /*
         * Other resposne:
         * 
            Explanation:
            - Moq Library: Used to create a mock object of IPayrollDatabase.
            - Setup Method: Initializes the mock and the service before each test.
            Test Cases:
            - Execute_ShouldAddEmployeeToDatabase: Verifies that the employee is added to the database with the correct properties.
            - Execute_ShouldReturnSuccessResultWithEmployeeId: Checks that the service returns a successful result with a valid employee ID.


            Assumptions:
            - The EmployeeFactory.Create method and other dependencies are assumed to work correctly.
            - The ServiceResult class has properties IsSuccess and EmployeeId.

            Ensure that any classes or interfaces like AddHourlyEmployeeServiceParams, IPayrollDatabase, ServiceResult, etc., are defined in your project with the expected behavior. Adjust mock setups and assertions as needed to fit your actual implementations.
         */

        #region Prompt: could you give me more test cases?

        [Test]
        public void Execute_ShouldThrowException_WhenNameIsNull()
        {
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = null,
                Address = "123 Main St",
                HourlyRate = 25.0m
            };

            Assert.Throws<ArgumentNullException>(() => _service.Execute(serviceParams));
        }

        [Test]
        public void Execute_ShouldThrowException_WhenAddressIsNull()
        {
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = null,
                HourlyRate = 25.0m
            };

            Assert.Throws<ArgumentNullException>(() => _service.Execute(serviceParams));
        }

        [Test]
        public void Execute_ShouldThrowException_WhenHourlyRateIsNegative()
        {
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = "123 Main St",
                HourlyRate = -5.0m
            };

            Assert.Throws<ArgumentOutOfRangeException>(() => _service.Execute(serviceParams));
        }

        [Test]
        public void Execute_ShouldAddEmployeeWithDefaultScheduleAndMethod()
        {
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "Jane Doe",
                Address = "456 Elm St",
                HourlyRate = 30.0m
            };

            _service.Execute(serviceParams);

            _mockDatabase.Verify(
                db => db.AddEmployee(It.Is<Employee>(e => (e.Schedule is WeeklySchedule) && (e.Method is HoldMethod))),
                Times.Once);
        }

        /*
         * Other responses:
         * 
         * Explanation:
           - Null Name: Verifies that an exception is thrown if the employee's name is null.
           - Null Address: Verifies that an exception is thrown if the employee's address is null.
           - Negative Hourly Rate: Verifies that an exception is thrown if the hourly rate is negative.
           - Default Schedule and Method: Ensures that the employee is assigned a WeeklySchedule and HoldMethod by default.


            Assumptions:
            - Exceptions like ArgumentNullException and ArgumentOutOfRangeException are thrown by the Execute method when encountering invalid parameters.
            - Adjust exception types and conditions based on actual method logic.

            These tests help ensure robust error handling and correct default behavior in the AddHourlyEmployeeService.
         */

        #endregion

        #region Prompt: let me give you more context data: [AddHourlyEmployeeServiceParams.cs], [HourlyClassification.cs], [ServiceResult.cs]. could you redesign the test cases for me?

        [Test]
        public void Execute_ShouldAddEmployeeToDatabase()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = "123 Main St",
                HourlyRate = 25.0m
            };

            // Act  
            ServiceResult result = _service.Execute(serviceParams);

            // Assert  
            Assert.IsTrue(result.Success);
            _mockDatabase.Verify(
                db => db.AddEmployee(
                    It.Is<Employee>(
                        e => (e.Name == serviceParams.Name) &&
                            (e.Address == serviceParams.Address) &&
                            (e.Classification is HourlyClassification) &&
                            (((HourlyClassification)e.Classification).HourlyRate == serviceParams.HourlyRate) &&
                            (e.Schedule is WeeklySchedule) &&
                            (e.Method is HoldMethod))),
                Times.Once);
        }

        [Test]
        public void Execute_ShouldReturnSuccessResultWithEmployeeId()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "Jane Doe",
                Address = "456 Elm St",
                HourlyRate = 30.0m
            };

            // Act  
            ServiceResult result = _service.Execute(serviceParams);

            // Assert  
            Assert.IsTrue(result.Success);
            Assert.AreNotEqual(Guid.Empty, result.ObjectId);
        }

        [Test]
        public void Execute_ShouldThrowException_WhenNameIsNull()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = null,
                Address = "123 Main St",
                HourlyRate = 25.0m
            };

            // Act & Assert  
            ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => _service.Execute(serviceParams));
            Assert.AreEqual("Name cannot be null", ex.ParamName);
        }

        [Test]
        public void Execute_ShouldThrowException_WhenAddressIsNull()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = null,
                HourlyRate = 25.0m
            };

            // Act & Assert  
            ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => _service.Execute(serviceParams));
            Assert.AreEqual("Address cannot be null", ex.ParamName);
        }

        [Test]
        public void Execute_ShouldThrowException_WhenHourlyRateIsNegative()
        {
            // Arrange  
            AddHourlyEmployeeServiceParams serviceParams = new AddHourlyEmployeeServiceParams
            {
                Name = "John Doe",
                Address = "123 Main St",
                HourlyRate = -5.0m
            };

            // Act & Assert  
            ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
                () => _service.Execute(serviceParams));
            Assert.AreEqual("HourlyRate must be positive", ex.ParamName);
        }

        /*
         * Explanation:
            - Valid Employee Addition: Verifies that the employee is added to the database with correct properties.
            - Success Result: Ensures the service returns a successful result with a valid employee ID.
            - Null Checks: Verifies that an exception is thrown if Name or Address is null.
            - Negative Rate: Verifies that an exception is thrown if the HourlyRate is negative.


            Assumptions:
            - The Execute method throws ArgumentNullException or ArgumentOutOfRangeException for invalid parameters.
            - Exception messages are descriptive (e.g., "Name cannot be null").

            Adjust the implementation of Execute if necessary to match these test cases.
         */

        #endregion
    }
}
